(function ($, exports) {
    var PREFIX = "octotree",
        EVENT = {
            TOGGLE: 'sidebar:toggle'
        },
    // HOTKEYS = '⌘+⇧+s, ⌃+⇧+s',
        HOTKEYS = '⌘+1, alt+1',
        TEMPLATE = '<div>\n' +
            '  <nav class="octotree_sidebar">\n' +
            '    <a class="octotree_toggle button"><div class="loader"></div><span></span></a>\n' +
            '    <a class="octotree_opts"><span></span></a>\n' +
            '    <div class="octotree_views">\n' +
            '      <div class="octotree_view octotree_treeview current">\n' +
            '        <div class="octotree_view_header"></div>\n' +
            '        <div class="octotree_view_body"></div>\n' +
            '      </div>\n' +
            '\n' +
            '      <div class="octotree_view octotree_errorview">\n' +
            '        <div class="octotree_view_header"></div>\n' +
            '        <form class="octotree_view_body">\n' +
            '          <div class="message"></div>\n' +
            '          <div>\n' +
            '            <input name="token" type="text" placeholder="Paste access token here" autocomplete="off">\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <button type="submit" class="button">Save</button>\n' +
            '            <a href="https://github.com/buunguyen/octotree#github-access-token" target="_blank" tabIndex="-1">Why is this required?</a>\n' +
            '          </div>\n' +
            '          <div class="error"></div>\n' +
            '        </form>\n' +
            '      </div>\n' +
            '\n' +
            '      <div class="octotree_view octotree_optsview">\n' +
            '        <div class="octotree_view_header">Settings</div>\n' +
            '        <form class="octotree_view_body">\n' +
            '          <div>\n' +
            '            <div>\n' +
            '              <label>Site access token</label>\n' +
            '              <a href="https://github.com/buunguyen/octotree#github-access-token" target="_blank" tabIndex="-1">what\'s this?</a>\n' +
            '            </div>\n' +
            '            <input type="text" data-store="TOKEN" data-perhost="true">\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <div>\n' +
            '              <label>GitHub Enterprise URLs</label>\n' +
            '            </div>\n' +
            '            <textarea data-store="GHEURLS" placeholder="https://github.mysite1.com/                                        https://github.mysite2.com/">\n' +
            '            </textarea>\n' +
            '          </div>\n' +
            '          <hr />\n' +
            '          <div>\n' +
            '            <label><input type="checkbox" data-store="REMEMBER"> Show sidebar if previously shown</label>\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <label><input type="checkbox" data-store="NONCODE"> Show in non-code pages</label>\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <label><input type="checkbox" data-store="LAZYLOAD"> Only load tree when sidebar is open</label>\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <label><input type="checkbox" data-store="COLLAPSE"> Collapse folders with single sub-folder</label>\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <div>\n' +
            '              <label>Hotkeys</label>\n' +
            '              <a href="https://github.com/madrobby/keymaster#defining-shortcuts" target="_blank" tabIndex="-1">supported keys</a>\n' +
            '            </div>\n' +
            '            <input type="text" data-store="HOTKEYS">\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <div>\n' +
            '              <label>Tab size</label>\n' +
            '              <a href="https://github.com/tiimgreen/github-cheat-sheet#adjust-tab-space" target="_blank" tabIndex="-1">what\'s this?</a>\n' +
            '            </div>\n' +
            '            <input type="text" data-store="TABSIZE">\n' +
            '          </div>\n' +
            '          <div>\n' +
            '            <button type="submit" class="button">Save</button>\n' +
            '          </div>\n' +
            '        </form>\n' +
            '      </div>\n' +
            '    </div>\n' +
            '  </nav>\n' +
            '\n' +
            '  <div class="octotree_popup">\n' +
            '    <div class="arrow"></div>\n' +
            '    <div class="content">\n' +
            '      Octotree is enabled on GitHub pages. Click this button or press\n' +
            '\n' +
            '      <kbd>cmd shift s</kbd> (or <kbd>ctrl shift s</kbd>)\n' +
            '      to show it.\n' +
            '    </div>\n' +
            '  </div>\n' +
            '</div>',
        GH_CONTAINERS = 'body  .container';

    function Finder() {
        this.extra = {};
    }

    $.extend(Finder.prototype, {
        selectPath: function (path, tabSize) {
            var me = this;

            $.ajax({
                url: 'cat',
                method: 'post',
                data: $.extend({}, me.extra, { path: path })
            }).done(function (html) {
                $('#c').text(html);
                hljs && hljs.highlightBlock($('#c')[0]);
            })
        },
        fetchData: function (opts, callback) {
            var me = this,
                path = opts.path;

            $.ajax({
                url: 'ls',
                method: 'post',
                data: $.extend({}, me.extra, { path: path })
            }).done(function (data) {
                callback(null, data.tree);
            });
        },
        updateLayout: function (sidebarVisible, sidebarWidth) {
            var $containers = $(GH_CONTAINERS),
                spacing = 10,
                autoMarginLeft,
                shouldPushLeft;

            // if ($containers.length === 4) {
            if ($containers.length) {
                autoMarginLeft = ($('body').width() - $containers.width()) / 2;
                shouldPushLeft = sidebarVisible && (autoMarginLeft <= sidebarWidth + spacing);
                $containers.css('margin-left', shouldPushLeft ? sidebarWidth + spacing : autoMarginLeft)
            } else {
                // falls-back if Adapter DOM has been updated
                $('html').css('margin-left', sidebarVisible ? sidebarWidth - spacing : 0)
            }
        }
    });


    // 树形菜单
    function TreeView($dom, adapter) {
        var me = this;

        me.$view = $dom.find('.octotree_treeview');
        me.adapter = adapter;
        me.extra = {};
        me.$view.find('.octotree_view_body')
            .on('click.jstree', '.jstree-open>a', function () {
                $.jstree.reference(this).close_node(this)
            })
            .on('click.jstree', '.jstree-closed>a', function () {
                $.jstree.reference(this).open_node(this)
            })
            .on('click', function (event) {
                var $target = $(event.target),
                    href, $icon;

                $target = $target.is('i.jstree-icon') ? $target.parent() : $target;

                if (!$target.is('a.jstree-anchor')) {
                    return;
                }

                href = $target.attr('href');
                $icon = $target.children().length ? $target.children(':first') : $target.siblings(':first');

                if ($icon.hasClass('commit')) {
                    // adapter.selectSubmodule(href);
                } else if ($icon.hasClass('blob')) {
                    adapter.selectPath(href/*, store.get(STORE.TABSIZE)*/);
                }
            })
            .jstree({
                core: {
                    multiple: false,
                    themes: { responsive: false },
                    data: function (node, cb) {
                        adapter.fetchData({
                            path: '#' === node.id ? '/' : node.id.substring(PREFIX.length)
                        }, function (err, tree) {
                            // 处理树结构
                            var folder = $.map(tree, function (item) {
                                var index = item.path.lastIndexOf('/');
                                item.id = PREFIX + item.path;
                                item.text = item.path.substring(index + 1);
                                item.icon = item.type;
                                item.children = 'tree' === item.type;
                                if ('blob' === item.type) {
                                    item.a_attr = {href: item.path}
                                }
                                return item;
                            });

                            cb(sort(folder));
                        });

                        // 排序, 文件夹优先, 文件名其次
                        function sort(folder) {
                            if (!folder) {
                                return folder;
                            }
                            folder.sort(function (a, b) {
                                if (a.type === b.type) {
                                    return a.text === b.text ? 0 : a.text < b.text ? -1 : 1
                                }
                                return 'blob' === a.type ? 1 : -1
                            });
                            folder.forEach(function (item) {
                                var isArray = Array.isArray || (function (o) {
                                    return '[object Array]' === Object.prototype.toString.call(o);
                                });
                                if ('tree' === item.type && isArray.call(Array, item.children)) {
                                    sort(item.children)
                                }
                            });
                            return folder
                        }
                    }
                },
                plugins: ['wholerow']
            });
    }

    $.extend(TreeView.prototype, {
        showHeader: function (repo) {
            var me = this,
                adapter = me.adapter;

            me.$view.find('.octotree_view_header')
                .html(
                    '<div class="octotree_header_repo">' +
                    '<a href="/' + repo.username + '">' + repo.username + '</a>' +
                    ' / ' +
                    '<a data-pjax href="/' + repo.username + '/' + repo.reponame + '">' + repo.reponame + '</a>' +
                    '</div>' +
                    '<div class="octotree_header_branch">' +
                    repo.branch +
                    '</div>'
            )
                .on('click', 'a[data-pjax]', function (event) {
                    event.preventDefault()
                    adapter.selectPath($(this).attr('href') /* a.href always return absolute URL, don't want that */)
                })
        },
        refresh: function () {
            this.$view.find('.octotree_view_body').jstree(true).refresh();
        },
        isActived: function () {
            var activeEl = document.activeElement,
                tree = this.$view.find('.octotree_view_body')[0];
            return tree == activeEl || tree.contains(activeEl);
        },
        focus: function () {
            this.$view.find('.octotree_view_body').focus();
        },
        blur: function () {
            this.isActived() && document.activeElement.blur();
        }
    });

    var win = window,
        $win = $(win),
        $doc = $(document),
        $bd = $(document.body),
        $dom = $(TEMPLATE),
        $sidebar = $dom.find('.octotree_sidebar'),
        $toggler = $dom.find('.octotree_toggle'),
        adapter = new Finder(),
        treeView = new TreeView($dom, adapter);

    // 布局改变
    $sidebar.appendTo($bd)
        .width(parseFloat(300))
        // .resizable({handlers: 'e', minWidth: 200})
        .resize(layoutChanged);

    $win.resize(function () {
        event.target === win && layoutChanged();
    });

    $doc.on(EVENT.TOGGLE, layoutChanged);

    // 显示/隐藏
    $toggler.click(toggleSidebar);
    key(HOTKEYS, function (event) {
        // 显示但是没有获取焦点
        if (isSidebarVisible() && !treeView.isActived()) {
            treeView.focus();
        } else {
            toggleSidebar();
        }
        event.preventDefault && event.preventDefault();
        return false;
    });

    exports.finder = adapter;
    exports.tree = treeView;

    /* ********************************** */

    // 布局改变处理
    function layoutChanged() {
        var w = $sidebar.width();
        adapter.updateLayout(isSidebarVisible(), w);
    }

    // sidebar 是否可见的
    function isSidebarVisible() {
        return $bd.hasClass(PREFIX);
    }

    // 显示隐藏 sidebar
    function toggleSidebar(visibility) {
        if (undefined !== visibility) {
            visibility == isSidebarVisible() || toggleSidebar();
        } else {
            $bd.toggleClass(PREFIX);
            $doc.trigger(EVENT.TOGGLE, isSidebarVisible());

            if (isSidebarVisible()) {
                // treeView.refresh();
                treeView.focus();
            } else {
                treeView.blur();
            }
        }
    }
})(jQuery, window);
